package tunnel;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Date;
import java.util.Vector;

import des.DesEncryptDecrypt;


public class LocalSocket extends Thread {
	public static final int MODE_CLIENT=1;
	public static final int MODE_SERVER=2;
	
	int mode;
	
	ServerSocket localSocket;
	RemoteSocket remoteSocket;
	String pw;
	String client = null;
	
	String remotehost;
	int remoteport;
	int port;
	
	boolean run = true;
	
	public LocalSocket(String remotehost, int remoteport, String pw, int mode) {
		this(8888, remotehost, remoteport, pw, mode);
	}
	
	public LocalSocket(int port, String remotehost, int remoteport, String pw, int mode) {
		this(port, remotehost, remoteport, pw, null, mode);
		
	}
	
	public LocalSocket(int port, String remotehost, int remoteport, String pw, String client, int mode) {
		this.pw = pw;
		this.client = client;
		this.remotehost = remotehost;
		this.remoteport = remoteport;
		this.mode = mode;
		this.port = port;
	}
	
	/*private void sendData(Vector<String> rec, Socket client) throws IOException {
		OutputStream out = client.getOutputStream();
		OutputStreamWriter osw = new OutputStreamWriter(out);
		
		
		for (String string : rec) {
			osw.append(string);
		}
		osw.flush();
		rec.clear();
	}*/
	private void sendData(byte[] bytes, Socket client) throws IOException {
		OutputStream out = client.getOutputStream();
		System.out.println("Sende an lokalen Port(" + bytes.length + ")" + " unverschlüsselt " + new Date());
		//System.out.println("Sende: " + new String(bytes));
		out.write(bytes);
		
		out.flush();
	}
	
	private void sendDataDES(Vector<Integer> rec, Socket client) throws IOException {
		byte[] send = new byte[rec.size()];
		for(int i=0; i<rec.size();i++) {
			send[i] = (byte)((int)rec.get(i));
		}
		System.out.println("Sende an lokalen Port(" + rec.size() + ")" + " verschlüsselt " + new Date());
		OutputStream out = client.getOutputStream();
		try {
			out.write(DesEncryptDecrypt.encode(send, pw ));
			out.flush();
			rec.clear();
		} catch (Exception e) {
			e.printStackTrace();
		}
		out.flush();
		rec.clear();
	}	
	
	private byte[] decodeDES(Vector<Integer> in) {
		System.out.println("Decodiere " + in.size() + " Elemente");
		byte[] raw = new byte[in.size()];
		
		for(int i=0;i<in.size();i++) {
			raw[i] = (byte)((int)in.get(i));
		}
		in.clear();
		try {
			return DesEncryptDecrypt.decode(raw, pw);
		} catch (Exception e) {
			System.out.println(e.getMessage());
			return null;
		}
	}
	
	@Override
	public void run() {
		remoteSocket = new RemoteSocket(remotehost, remoteport);
		
		try {
			localSocket = new ServerSocket(port);
		} catch (IOException e) {
			System.out.println("Konnte lokalen Server nicht aufmachen");
			e.printStackTrace();
		}
		
		System.out.println("Starte Tunnel");
		System.out.println("Lausche auf: " + port);
		System.out.println("Sende an: " + remotehost + ":" + remoteport);
		System.out.println();
		
		Socket client=null;
		while(run) {
			try {
				
				localSocket.setSoTimeout(9000);
				client = localSocket.accept();
				client.setReuseAddress(true);
				remoteSocket.connect();
				
				if(this.client != null && !client.getInetAddress().getHostAddress().equals(this.client)) {
					System.out.println("Unberechtigter Versuch des Zugriffs");
					continue;
				}
				
				InputStream  in  = client.getInputStream();
				int length;
				byte[] zusenden;
				if(mode == MODE_CLIENT) {
					zusenden = new byte[900000];
					length = in.read(zusenden);
				} else { 
					zusenden = new byte[900000];
					try {
						length = in.read(zusenden);
						zusenden = DesEncryptDecrypt.decode(zusenden, length, pw );
					} catch (Exception e) {
						e.printStackTrace();
					}
					length = zusenden.length;
				}
				
				if(length != -1) {
					System.out.println(length);
					
					if(mode == MODE_CLIENT) {
						remoteSocket.sendDataDESEncrypted(zusenden, length, pw);
					} else {
						remoteSocket.sendData(zusenden, length);
					}
					
					if(mode == MODE_CLIENT) {
						int received;
						Vector<Integer> rec = new Vector<Integer>();
						while((received = remoteSocket.readData()) != -1) {
							if(received != -2)
								rec.add(received);
							if(rec.size() >= 4008) { // 8 Bytes mehr für DES Verschlüsselung
								sendData(decodeDES(rec), client);
							}
						}
						sendData(decodeDES(rec), client);
					} else {
						int received;
						Vector<Integer> rec = new Vector<Integer>();
						while((received = remoteSocket.readData()) != -1) {
							if(received != -2)
								rec.add(received);
							if(rec.size() >= 4000) {
								sendDataDES(rec, client);
							}
						}
						sendDataDES(rec, client);
					}
					
				}
				client.close();
				remoteSocket.close();
				System.out.println("Fertig " + mode + " " + new Date());
			} catch (SocketTimeoutException e) {
				
			} catch (IOException e) {
				System.out.println("TimeOut beim warten auf lokale Verbindung");
				e.printStackTrace();
			} finally {
				remoteSocket.close();
				try {
					if(client != null)
						client.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

		}
		
		try {
			remoteSocket.close();
			localSocket.close();
			if(client != null)
				client.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void stopTunnel() {
		run = false;
	}
	
	public static void main(String[] args) {
		LocalSocket tunnel = new LocalSocket("127.0.0.1", 8888, "bldfjhhg", LocalSocket.MODE_CLIENT);
		tunnel.start();
		while(true);
	}

}
